﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using PersonalWebSite.Common;
using PersonalWebSite.Common.Google.DTO;
using System.Net;
using System.IO;
using System.Web.Script.Serialization;
using log4net;

namespace PersonalWebSite.Common.Google
{
    /// <summary>
    /// Defines methods for Google Plus API
    /// </summary>
    public class GooglePlusManager
    {
        #region Variables
        protected const string userId = "102916204430573119519";
        protected const int pageSize = 10;
        protected const string apiKey = "AIzaSyDrbOWX5e4i1tZnGEx2n-dZ5YXrFsZD4SE";
        protected static readonly TimeSpan activitiesCacheDuration = TimeSpan.FromHours(2);

        private const string cacheKeyActivityFeed = "GooglePlusActivityFeed";
        private static readonly ILog logger = LogManager.GetLogger(typeof(GooglePlusManager));
        #endregion

        #region Static Constructor
        /// <summary>
        /// Inits the static class
        /// </summary>
        static GooglePlusManager()
        {
            log4net.Config.XmlConfigurator.Configure();
        }
        #endregion

        /// <summary>
        /// Fethces the Google Plus Activity Feed
        /// </summary>
        /// <returns>Google Plus Activity Feed</returns>
        public static ActivityFeed FetchActivityFeed(int pageIndex = 0)
        {
            int currentPageSize = pageSize;

            ActivityFeed activityFeed = Cache.CacheItem(cacheKeyActivityFeed, () => FetchActivityFeedFromGoogleDataApi(), activitiesCacheDuration);

            if (activityFeed.NextPageToken != null)
            {
                int requiredArticles = (pageIndex + 1) * currentPageSize;
                if (activityFeed.Items.Length < requiredArticles)
                {
                    try
                    {
                        ActivityFeed activityFeedNextPage = FetchActivityFeedFromGoogleDataApi(activityFeed.NextPageToken);
                        currentPageSize = activityFeedNextPage.Items == null ? 0 : activityFeedNextPage.Items.Length;
                        activityFeed.Items = activityFeed.Items.Union(activityFeedNextPage.Items).ToArray();
                        activityFeed.NextPageToken = activityFeedNextPage.NextPageToken;
                        activityFeed.LastPageIndex = activityFeed.NextPageToken != null ? pageIndex : activityFeed.LastPageIndex;
                        activityFeed.LastPageSize = activityFeed.NextPageToken != null ? currentPageSize : activityFeed.LastPageSize;
                    }
                    catch (Exception ex)
                    {
                        logger.Error(string.Format("Error occured while trying to fetch Next Page Activity Feed from Google Plus; Next Page Token: {0}; Current Feed Activity Items Count: {1}", activityFeed.NextPageToken, activityFeed.Items.Length), ex);
                    }
                }
            }

            pageIndex = pageIndex > activityFeed.LastPageIndex ? activityFeed.LastPageIndex : pageIndex;
            currentPageSize = currentPageSize == 0 ? activityFeed.LastPageSize : currentPageSize;

            activityFeed.ItemsToDisplay = activityFeed.Items.Skip(pageIndex * currentPageSize).Take(currentPageSize).ToArray();

            return activityFeed;
        }

        #region Private Helper Methods
        /// <summary>
        /// Tries to fetch the Activity feed from Google
        /// </summary>
        /// <param name="pageToken">Page token to include in the request</param>
        /// <returns>Activity feed</returns>
        private static ActivityFeed FetchActivityFeedFromGoogleDataApi(string pageToken = null)
        {
            string pageTokenString = pageToken == null ? string.Empty : "&pageToken=" + pageToken;
            string googleActivitiesUrl = string.Format("https://www.googleapis.com/plus/v1/people/{0}/activities/public?alt=json&maxResults={1}&pp=1&key={2}{3}", userId, pageSize, apiKey, pageTokenString);
            //string googleActivitiesUrl = "file:///C:/Users/Dan/Projects/Personal%20site/The%20Site/MVC%20Solution/PersonalWebSite/PersonalWebSite/TestData/GooglePlusActivityFeedSample.json";
            try
            {
                WebRequest request = WebRequest.Create(googleActivitiesUrl);
                WebResponse response = request.GetResponse();
                string responseContent = null;
                using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
                {
                    responseContent = streamReader.ReadToEnd();
                }
                Dictionary<string, object> jSonData = (Dictionary<string, object>)new JavaScriptSerializer().DeserializeObject(responseContent);

                return new ActivityFeed
                {
                    DateUpdated = DateTimeExtensions.ParseAndFallback((string)jSonData.Val("updated")),
                    ETag = (string)jSonData.Val("etag"),
                    Id = (string)jSonData.Val("id"),
                    NextLink = (string)jSonData.Val("nextLink"),
                    NextPageToken = (string)jSonData.Val("nextPageToken"),
                    SelfLink = (string)jSonData.Val("selfLink"),
                    Title = (string)jSonData.Val("title"),
                    LastPageIndex = 0,

                    Items = (from i in (object[])jSonData.Val("items")
                             let item = (Dictionary<string, object>)i
                             let o = (Dictionary<string, object>)item.Val("object")
                             select new ActivityItem
                             {
                                 DatePublished = DateTimeExtensions.ParseAndFallback((string)item.Val("published") ?? DateTime.Now.ToString()),
                                 DateUpdated = DateTimeExtensions.ParseAndFallback((string)item.Val("updated") ?? DateTime.Now.ToString()),
                                 Address = (string)item.Val("address"),
                                 Annotation = (string)item.Val("annotation"),
                                 ETag = (string)item.Val("etag"),
                                 Geocode = Geocode.TryParse((string)item.Val("geocode")),
                                 Id = (string)item.Val("id"),
                                 PlaceId = (string)item.Val("placeId"),
                                 PlaceName = (string)item.Val("placeName"),
                                 Title = (string)item.Val("title"),
                                 Url = (string)item.Val("url"),
                                 Object = new ActivityItem.ActivityObject
                                 {
                                     Content = (string)o.Val("content"),
                                     Id = (string)o.Val("id"),
                                     OriginalContent = (string)o.Val("originalContent"),
                                     Type = (ActivityItem.ActivityObjectType)Enum.Parse(typeof(ActivityItem.ActivityObjectType), (string)o.Val("objectType") ?? ActivityItem.ActivityObjectType.Activity.ToString(), true),
                                     Url = (string)o.Val("url"),
                                     Attachements = (from a in (object[])o.Val("attachments")
                                                     let att = (Dictionary<string, object>)a
                                                     let embed = (Dictionary<string, object>)att.Val("embed")
                                                     let fullImage = (Dictionary<string, object>)att.Val("fullImage")
                                                     let image = (Dictionary<string, object>)att.Val("image")
                                                     select new ActivityItem.Attachement
                                                     {
                                                         AttachementObjectType = (ActivityItem.AttachementObjectType)Enum.Parse(typeof(ActivityItem.AttachementObjectType), ((string)att.Val("objectType") ?? ActivityItem.AttachementObjectType.Article.ToString()).Replace("-", string.Empty), true),
                                                         Content = (string)att.Val("content"),
                                                         DisplayName = (string)att.Val("displayName"),
                                                         Id = (string)att.Val("id"),
                                                         Embed = new ActivityItem.Embed
                                                         {
                                                             Type = embed == null ? null : (string)embed.Val("type"),
                                                             Url = embed == null ? null : (string)embed.Val("url")
                                                         },
                                                         FullImage = new ActivityItem.Image
                                                         {
                                                             Height = fullImage == null ? 0 : (int)(fullImage.Val("height") ?? -1),
                                                             Width = fullImage == null ? 0 : (int)(fullImage.Val("width") ?? -1),
                                                             Url = fullImage == null ? null : (string)fullImage.Val("url"),
                                                             Type = fullImage == null ? null : (string)fullImage.Val("type")
                                                         },
                                                         Image = new ActivityItem.Image
                                                         {
                                                             Height = image == null ? 0 : (int)(image.Val("height") ?? -1),
                                                             Width = image == null ? 0 : (int)(image.Val("width") ?? -1),
                                                             Url = image == null ? null : (string)image.Val("url"),
                                                             Type = image == null ? null : (string)image.Val("type")
                                                         },
                                                         Url = (string)att.Val("url")
                                                     }).ToArray()
                                 }
                             }).ToArray()
                };
            }
            catch (Exception ex)
            {
                logger.Error(string.Format("Error occured while trying to fetch Activity Feed from Google Plus; Requested URL: {0}", googleActivitiesUrl), ex);
                throw;
            }
        }
        #endregion
    }
}